import type { CreateAxiosDefaults, AxiosRequestConfig, AxiosResponse } from "axios"
import axios from "axios"
import { RequestMethod } from "../enum"
import { VAxios } from "./axios"
import { isFunction } from "@/utils"
import { AxiosTransform, CustomAxiosRequestConfig, CustomCreateAxiosDefaults } from "./axiosTransform"
import { ResponseData } from "@/hooks/useAxios"
import { RequestConfigExtra } from "../types"

/**
 * 封装各个请求方法及结果处理的类
 */
export class AxiosRequest {
    instance: VAxios

    constructor(instance: VAxios) {
        this.instance = instance
    }

    get<T = any>(url: string, params?: any, options?: RequestConfigExtra): Promise<T> {
        const config = {
            url,
            method: RequestMethod.GET,
            params
        }
        return this.request(config, options)
    }

    post<T = any>(url: string, params?: any, options?: RequestConfigExtra): Promise<T> {
        const config = {
            url,
            method: RequestMethod.POST,
            data: params
        }
        return this.request(config, options)
    }

    put<T = any>(url: string, params?: any, options?: RequestConfigExtra): Promise<T> {
        const config = {
            url,
            method: RequestMethod.PUT,
            data: params
        }
        return this.request(config, options)
    }

    delete<T = any>(url: string, params?: any, options?: RequestConfigExtra): Promise<T> {
        const config = {
            url,
            method: RequestMethod.DELETE,
            data: params
        }
        return this.request(config, options)
    }

    /**
     * @description:   请求方法
     */
    request<T = any>(config: AxiosRequestConfig, options?: RequestConfigExtra): Promise<T> {
        const configWrapper: AxiosRequestConfig & CustomAxiosRequestConfig = config
        const axiosInstance = this.instance.getAxios()
        const defaultOptions = this.instance.getDefaultOptions()
        const { transform, requestOptions } = defaultOptions
        const mergedOptions: RequestConfigExtra = Object.assign({}, requestOptions, options)
        const { beforeRequestHook, requestCatch, transformResponseData } = transform || {}
        beforeRequestHook && isFunction(beforeRequestHook) && beforeRequestHook(configWrapper, mergedOptions)
        configWrapper.requestOptions = mergedOptions

        return new Promise((resolve, reject) => {
            axiosInstance
                .request<any, AxiosResponse<ResponseData<T>>>(configWrapper)
                .then(res => {
                    // 请求是否被取消
                    const isCancel = axios.isCancel(res)
                    if (transformResponseData && isFunction(transformResponseData) && !isCancel) {
                        try {
                            const ret = transformResponseData(res, mergedOptions)
                            resolve(ret)
                        } catch (err) {
                            reject(err || new Error("response handle error!"))
                        }
                    } else {
                        resolve(res as unknown as Promise<T>)
                    }
                })
                .catch((e: Error) => {
                    console.log("request error")
                    if (requestCatch && isFunction(requestCatch)) {
                        reject(requestCatch(e))
                    } else {
                        reject(e)
                    }
                })
        })
    }
}

export function createAxiosRequest(config: CreateAxiosDefaults, transform: AxiosTransform, customOptions?: RequestConfigExtra) {
    const customConfig = config as CustomCreateAxiosDefaults
    customConfig.transform = transform
    customConfig.requestOptions = customOptions ?? {}
    return new AxiosRequest(new VAxios(customConfig))
}
